package cn.song.util;

import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

/**
 * 文件路径管理和图片压缩工具类
 *
 * <p>此工具类提供了完整的文件路径管理和图片处理功能，主要用于项目中的文件存储、
 * 路径转换和图片压缩等操作。支持跨平台文件路径处理和高效的图片压缩算法。</p>
 *
 * <p>主要功能模块：</p>
 * <ul>
 *   <li>路径管理 - 提供统一的文件路径常量和转换方法</li>
 *   <li>目录初始化 - 自动创建必要的工作目录结构</li>
 *   <li>图片压缩 - 支持多种图片格式的智能压缩</li>
 *   <li>文件名处理 - 提供时间戳文件名生成功能</li>
 *   <li>跨平台支持 - 自动适配不同操作系统的路径分隔符</li>
 * </ul>
 *
 * <p>路径常量说明：</p>
 * <ul>
 *   <li>ABSOLUTE_PATH - 基于系统临时目录的绝对路径</li>
 *   <li>DOWN_ABSOLUTE - 下载文件的绝对路径</li>
 *   <li>RELATIVE_PATH - Web访问的相对路径</li>
 *   <li>DOWN_RELATIVE - 下载文件的相对路径</li>
 * </ul>
 *
 * <p>图片压缩特性：</p>
 * <ul>
 *   <li>尺寸压缩 - 根据目标文件大小自动计算最佳尺寸</li>
 *   <li>质量保持 - 使用高质量的缩放算法保持图片清晰度</li>
 *   <li>格式支持 - 支持JPG、PNG、GIF等常见图片格式</li>
 *   <li>内存优化 - 使用BufferedImage提高处理效率</li>
 * </ul>
 *
 * @author SongRenShuo
 * @version 1.0
 * @since 2025-01-21
 * @see java.awt.image.BufferedImage
 * @see javax.imageio.ImageIO
 */
@Slf4j
public class FilePath {

    /**
     * 安全获取系统临时目录路径
     *
     * <p>此方法提供跨平台的临时目录获取功能，优先使用系统默认临时目录，
     * 如果获取失败则使用用户主目录下的temp子目录作为备用方案。</p>
     *
     * @return 临时目录的绝对路径字符串，保证返回值不为null
     * @since 1.0
     * @apiNote 此方法会自动处理不同操作系统的路径差异
     */
    private static String getTempDir() {
        String tempDir = System.getProperty("java.io.tmpdir");
        if (tempDir == null || tempDir.trim().isEmpty()) {
            // 如果系统属性为空，使用默认路径
            tempDir = System.getProperty("user.home") + File.separator + "temp";
        }
        return tempDir;
    }

    /**
     * 项目主要工作目录的绝对路径常量
     *
     * <p>基于系统临时目录构建的项目专用工作目录，用于存储项目运行过程中产生的临时文件、
     * 缓存文件和中间结果文件。路径格式为：{系统临时目录}/SpringbootExcelDemo/</p>
     */
    public static final String ABSOLUTE_PATH = getTempDir() + File.separator + "SpringbootExcelDemo" + File.separator;
    /**
     * 文件下载目录的绝对路径常量
     *
     * <p>专门用于存储用户下载文件的目录，基于主工作目录创建。
     * 路径格式为：{主工作目录}/down/</p>
     */
    public static final String DOWN_ABSOLUTE = ABSOLUTE_PATH + "down" + File.separator;

    /**
     * 项目Web访问的相对路径常量
     *
     * <p>用于Web应用中的文件访问，通常配合静态资源映射使用。
     * 路径格式为：/SpringbootExcelDemoFile/</p>
     */
    public static final String RELATIVE_PATH = "/SpringbootExcelDemoFile/";
    /**
     * 文件下载的Web相对路径常量
     *
     * <p>用于Web应用中的文件下载访问。
     * 路径格式为：/SpringbootExcelDemoFile/down/</p>
     */
    public static final String DOWN_RELATIVE = RELATIVE_PATH + "down/";

    static {
        // 初始化时创建必要的目录
        initDirectories();
    }

    /**
     * 初始化项目必要的目录结构
     *
     * <p>在类加载时自动执行，创建项目运行所需的基础目录结构。
     * 包括主工作目录和下载目录，如果目录创建失败会记录警告日志但不影响程序运行。</p>
     *
     * <p>创建的目录包括：</p>
     * <ol>
     *   <li>Ⅰ. 主工作目录 - 存储项目临时文件</li>
     *   <li>Ⅱ. 下载目录 - 存储用户下载的文件</li>
     * </ol>
     *
     * @since 1.0
     * @apiNote 此方法在类初始化时自动调用，无需手动执行
     */
    private static void initDirectories() {
        try {
            // Ⅰ. 创建项目主工作目录
            File mainDir = new File(ABSOLUTE_PATH);
            if (!mainDir.exists()) {
                boolean created = mainDir.mkdirs();
                if (created) {
                    log.info("成功创建主工作目录: {}", ABSOLUTE_PATH);
                } else {
                    log.warn("无法创建主工作目录: {}", ABSOLUTE_PATH);
                }
            }

            // Ⅱ. 创建文件下载专用目录
            File downDir = new File(DOWN_ABSOLUTE);
            if (!downDir.exists()) {
                boolean created = downDir.mkdirs();
                if (created) {
                    log.info("成功创建下载目录: {}", DOWN_ABSOLUTE);
                } else {
                    log.warn("无法创建下载目录: {}", DOWN_ABSOLUTE);
                }
            }
        } catch (Exception e) {
            log.error("初始化目录失败", e);
        }
    }

    /**
     * 将Web相对路径转换为本地绝对路径
     *
     * <p>此方法用于将Web应用中使用的相对路径转换为本地文件系统的绝对路径，
     * 便于Java程序直接访问文件。主要用于处理从前端传来的文件路径。</p>
     *
     * <p>转换规则：</p>
     * <ol>
     *   <li>Ⅰ. 将所有的“/”替换为“//”（转义处理）</li>
     *   <li>Ⅱ. 将项目相对路径前缀替换为绝对路径前缀</li>
     * </ol>
     *
     * @param relativePath Web相对路径，如"/SpringbootExcelDemoFile/down/file.xlsx"
     * @return 本地绝对路径，如"C:\\temp\\SpringbootExcelDemo\\down\\file.xlsx"
     * @since 1.0
     * @apiNote 转换后的路径可以直接用于创建File对象
     */
    public static String convertToAbsolutePath(String relativePath) {
        // Ⅰ. 先将所有的斜杠进行转义处理，避免路径分隔符冲突
        String processedPath = relativePath.replace("/", "//");

        // Ⅱ. 将项目相对路径前缀替换为对应的绝对路径前缀
        String convertedPath = processedPath.replace(
                FilePath.RELATIVE_PATH.replace("/", "//"),
                FilePath.ABSOLUTE_PATH
        );

        log.debug("路径转换: {} -> {}", relativePath, convertedPath);
        return convertedPath;
    }

    /**
     * 生成包含时间戳的唯一文件名
     *
     * <p>此方法在原始文件名后面附加当前时间戳，用于生成唯一的文件名，
     * 避免文件名冲突。常用于上传文件、导出文件等场景。</p>
     *
     * <p>时间戳格式：yyyyMMddHHmmSS（年月日时分秒）</p>
     *
     * @param fileName 原始文件名（不包含扩展名）
     * @return 包含时间戳的文件名，格式为"原文件名 + yyyyMMddHHmmSS"
     * @since 1.0
     * @author SongRenShuo
     * @apiNote 示例："report" -> "report20250121143025"
     */
    public static String getFileNameDate(String fileName) {
        // 使用Hutool的DateUtil生成标准格式的时间戳
        String timestamp = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmSS");
        String uniqueFileName = fileName + timestamp;

        log.debug("生成唯一文件名: {} -> {}", fileName, uniqueFileName);
        return uniqueFileName;
    }

    /**
     * 智能图片文件压缩处理方法
     *
     * <p>此方法提供了高效的图片压缩功能，可以将图片文件压缩到指定大小。
     * 支持多种图片格式，使用高质量的缩放算法保持图片清晰度。</p>
     *
     * <p>压缩流程：</p>
     * <ol>
     *   <li>Ⅰ. 提取文件扩展名用于格式检测</li>
     *   <li>Ⅱ. 使用ImageIO读取原始图片</li>
     *   <li>Ⅲ. 调用压缩算法进行尺寸和质量调整</li>
     *   <li>Ⅳ. 将压缩后的图片保存到原文件位置</li>
     * </ol>
     *
     * @param path 图片文件所在的目录路径（以文件分隔符结尾）
     * @param fileName 图片文件名（包含扩展名）
     * @throws IOException 当文件读写操作失败时抛出
     * @since 1.0
     * @apiNote 默认压缩目标为50KB，压缩后会覆盖原文件
     */
    public static void compressImageFile(String path, String fileName) throws IOException {
        // Ⅰ. 提取文件扩展名用于后续的格式处理
        File inputImagePath1 = new File(path + fileName);
        String fileName1 = inputImagePath1.getName();
        String fileExtension = "";

        // 使用最后一个点号作为扩展名分隔符
        int dotIndex = fileName1.lastIndexOf('.');
        if (dotIndex > 0) {
            fileExtension = fileName1.substring(dotIndex + 1);
        }

        log.debug("准备压缩图片: {}{}, 文件格式: {}", path, fileName, fileExtension);

        // Ⅱ. 构建输入和输出文件路径
        String inputImagePath = path + fileName;
        // 覆盖原文件
        String outputImagePath = path + fileName;

        // 设置默认的压缩目标大小为50KB
        long targetSizeKb = 50;

        // Ⅲ. 使用ImageIO读取原始图片数据
        BufferedImage image = ImageIO.read(new File(inputImagePath));
        if (image == null) {
            log.error("无法读取图片文件: {}", inputImagePath);
            return;
        }

        // Ⅳ. 调用压缩算法进行图片处理
        Image compressedImage = compressImage(image, targetSizeKb, fileExtension);

        // Ⅴ. 将压缩后的图片保存到指定位置
        boolean success = ImageIO.write((BufferedImage) compressedImage, fileExtension, new File(outputImagePath));

        if (success) {
            log.info("图片压缩完成: {}, 目标大小: {} KB", outputImagePath, targetSizeKb);
        } else {
            log.error("图片压缩失败: {}", outputImagePath);
        }
    }

    /**
     * 压缩图片
     * @param image 原始图片
     * @param targetSizeKb 目标大小（KB）
     * @param fileExtension 文件扩展名
     * @return 压缩后的图片
     */
    public static Image compressImage(BufferedImage image, long targetSizeKb, String fileExtension) {
        // 计算目标图片的尺寸
        long targetSizeBytes = targetSizeKb * 1024;
        long originalSizeBytes = getImageSize(image, fileExtension);
        double compressionRatio = (double) targetSizeBytes / originalSizeBytes;
        int targetWidth = (int) (image.getWidth() * Math.sqrt(compressionRatio));
        int targetHeight = (int) (image.getHeight() * Math.sqrt(compressionRatio));
        // 使用ImageIO进行压缩
        BufferedImage compressedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
        compressedImage.getGraphics().drawImage(image.getScaledInstance(targetWidth, targetHeight, Image.SCALE_SMOOTH), 0, 0, null);
        return compressedImage;
    }

    /**
     * 获取图片的大小
     * @param image 图片对象
     * @param fileExtension 文件扩展名
     * @return 图片大小（字节）
     */
    public static long getImageSize(BufferedImage image, String fileExtension) {
        File tempFile;
        try {
            tempFile = File.createTempFile("temp", ".tmp");
            ImageIO.write(image, fileExtension, tempFile);
            long size = tempFile.length();
            tempFile.delete();
            return size;
        } catch (IOException ex) {
            log.error("压缩图片异常：", ex);
            return 0;
        }
    }


}
